/*
 * @Author: hongbin
 * @Date: 2022-11-23 10:55:09
 * @LastEditors: hongbin
 * @LastEditTime: 2022-11-26 10:41:00
 * @Description:自定义形象页面
 */
import { FC, useCallback, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { addPartsToActor, init, removePartsFromActor } from "../src/CustomProfile";
import Option from "../src/components/CustomProfile/Option";
import PartsList from "../src/components/CustomProfile/PartsList";
import { IPart, IPartConfig, IParts, IUserPartsConfig } from "../src/components/CustomProfile/types";
import { get, post } from "../src/api";
import { showMessage } from "../src/HUI/MessageBar";
import Link from "next/link";

export const defaultConf: IPartConfig = {
    color: "#000000",
    opacity: 1,
    metalness: 0,
};

interface IProps {
    /**
     * 服务端查询 获取用户的配置列表后加载页面
     */
    data: IUserPartsConfig;
}

const CustomProfile: FC<IProps> = ({ data: serverData }) => {
    const canvasRef = useRef<HTMLCanvasElement>(null);
    const [parts, setParts] = useState<IParts>({});
    const [wearParts, setWearParts] = useState<IPart[]>([]);
    const [currentPartType, setCurrentPartType] = useState("");
    const [userPartsConfig, setUserPartsConfig] = useState(serverData);

    /**
     * 初始化场景 加载配件模型
     */
    useEffect(() => {
        if (!canvasRef.current) throw Error("获取canvas失败");
        init({
            antialias: true,
            canvas: canvasRef.current,
        });
        init.loadPartsModel.change((part) => {
            setParts((prev) => {
                const ps = { ...prev };
                ps[part.type] ? ps[part.type].push(part) : (ps[part.type] = [part]);
                return ps;
            });
            //如果当前启用的配置名包含这一配件
            if (serverData[serverData.current].wearParts.includes(part.name)) {
                setWearParts((prev) => [...prev, part]);
            }
        });

        return () => {
            init.clear();
            setParts({});
            setWearParts([]);
        };
    }, [serverData]);

    //默认选择第一个部位
    useEffect(() => {
        setCurrentPartType(Object.keys(parts)[0]);
    }, [parts]);

    /**
     * 配置变更后应用three.js切换配件
     */
    useEffect(() => {
        if (wearParts.length) {
            console.log("应用配置:", wearParts);
            addPartsToActor(wearParts, userPartsConfig[userPartsConfig.current]);
        }
        return () => {
            //去掉之前的
            removePartsFromActor(wearParts);
        };
    }, [wearParts, userPartsConfig]);

    /**
     * 选择配件 重复的视为换下 同时第一次获取没有配件的样式配置要添加默认的样式
     */
    const selectPart = useCallback(
        (part: IPart) => {
            // 判断当前配置中是否有这一项的配置
            if (!userPartsConfig[userPartsConfig.current][part.name]) {
                setUserPartsConfig((prev) => ({
                    ...prev,
                    [prev.current]: { ...prev[prev.current], [part.name]: defaultConf },
                }));
            }
            setWearParts((prev) => {
                const wears = prev.filter((p) => p.type !== part.type);
                //重复的去掉
                const isRepeat = prev.some((p) => p.name === part.name);
                const wearParts = isRepeat ? wears : [...wears, part];

                return wearParts;
            });
        },
        [userPartsConfig]
    );

    /**
     * 将当前配置保存进数据库
     */
    const saveConfig = async () => {
        const currConfig = { ...userPartsConfig[userPartsConfig.current], wearParts: wearParts.map((p) => p.name) };
        console.log(currConfig);
        const { data } = await post("/PartsConfig/save", {
            userId: userPartsConfig.userId,
            config: { ...userPartsConfig, [userPartsConfig.current]: currConfig },
        });
        console.log("应用结果：", data);

        showMessage(data ? "应用成功🎉" : "保存失败");
    };

    /**
     * 切换配置 穿戴配件转换到目标配置 切换current属性
     */
    const toggleConfig = useCallback(
        (name: "config1" | "config2" | "config3") => {
            console.log(name);
            setUserPartsConfig((prev) => {
                const allPart = Object.values(parts).flat(1);

                const newWearPartsConfig = [] as IPart[];
                prev[name].wearParts.forEach((wearPartName) => {
                    const part = allPart.find((p) => p.name == wearPartName);
                    if (part) newWearPartsConfig.push(part);
                });
                setWearParts(newWearPartsConfig);

                return {
                    ...prev,
                    //保留当前穿戴配置
                    [prev.current]: { ...prev[prev.current], wearParts: wearParts.map((p) => p.name) },
                    current: name as unknown as any,
                };
            });
        },
        [wearParts, parts]
    );

    if (!serverData) return <span>查询失败</span>;

    return (
        <Container>
            <Back>
                <Link href="/">返回</Link>
            </Back>
            <canvas ref={canvasRef}></canvas>
            <Side>
                <PartsList
                    wearParts={wearParts}
                    onSelect={selectPart}
                    list={parts[currentPartType]}
                    // partsConfig={partsConfig}
                    partsConfig={userPartsConfig[userPartsConfig.current]}
                    setUserPartsConfig={setUserPartsConfig}
                />
                <Option
                    saveConfig={saveConfig}
                    parts={Object.keys(parts)}
                    currentPartType={currentPartType}
                    onChange={setCurrentPartType}
                    toggleConfig={toggleConfig}
                    currentConfig={userPartsConfig.current}
                />
            </Side>
        </Container>
    );
};

/**
 * 服务端执行 获取用户配置
 */
export const getServerSideProps = async ({ query: { id } }: any) => {
    const { data } = await get("/PartsConfig/query?id=" + id);
    console.log("用户id:", id);

    const props = {} as { data: IUserPartsConfig };

    if (data && data.current) props.data = data;

    return {
        props,
    };
};

export default CustomProfile;

const Back = styled.span`
    position: fixed;
    top: 0.5vh;
    left: 0.5vw;
    color: #fffae5;
    font-size: 1.5vw;
    cursor: pointer;
    padding: 1vh 1vw;
    background: #1d450b;
    border-radius: 0.5vw;
`;

const Side = styled.aside`
    background: #fff;
    width: 30%;
    display: flex;
`;

const Container = styled.div`
    display: flex;
    width: 100vw;
    height: 100vh;

    canvas {
        background: black;
        width: 70% !important;
        height: 100% !important;
    }
`;
